/**
 * All rights reserved.
 *
 * File：DataRepositoryExtension.java
 * History:
 *         2018年11月9日: Initially created, Chrise.
 */
package com.pwt.repository.base;


import com.pwt.common.ResultPage;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.query.internal.QueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;

import javax.annotation.PostConstruct;
import javax.persistence.*;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * 数据仓库扩展。
 * @author Chrise
 */
@SuppressWarnings({"unchecked", "rawtypes"})
public abstract class DataRepositoryExtension<T, ID extends Serializable> {
	private static final int DEF_PAGE_INDEX = 0;
	private static final int DEF_PAGE_SIZE = 50;
	
	@PersistenceContext
	private EntityManager entityManager;
	private Class<T> entityClass;
	private JpaEntityInformation<T, ?> entityInformation;
	
	/**
	 * 初始化。
	 * @author Chrise 2018年11月9日
	 */
	@PostConstruct
	private void initialize() {
		this.entityClass = (Class<T>)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		this.entityInformation = JpaEntityInformationSupport.getEntityInformation(this.entityClass, this.entityManager);
	}
	
	/**
	 * 获取实体管理器。
	 * @author Chrise 2018年11月9日
	 * @return 实体管理器对象。
	 */
	protected EntityManager getEntityManager() {
		return this.entityManager;
	}
	
	/**
	 * 获取实体类。
	 * @author Chrise 2018年11月9日
	 * @return 实体类对象。
	 */
	protected Class<T> getEntityClass() {
		return this.entityClass;
	}
	
	/**
	 * 获取实体信息。
	 * @author Chrise 2018年11月9日
	 * @return 实体信息对象。
	 */
	protected JpaEntityInformation<T, ?> getEntityInformation() {
		return this.entityInformation;
	}
	
	/**
	 * 查询实体。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 实体对象。
	 */
	protected T queryEntity(String jpql, Object... params) {
		TypedQuery<T> query = this.createQuery(false, this.entityClass, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询实体。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 实体对象。
	 */
	protected T queryEntity(String jpql, Map<String, Object> params) {
		TypedQuery<T> query = this.createQuery(false, this.entityClass, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询实体集合。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 实体集合对象。
	 */
	protected List<T> queryEntityList(String jpql, Object... params) {
		TypedQuery<T> query = this.createQuery(false, this.entityClass, jpql, params);
		return query.getResultList();
	}
	
	/**
	 * 查询实体集合。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 实体集合对象。
	 */
	protected List<T> queryEntityList(String jpql, Map<String, Object> params) {
		TypedQuery<T> query = this.createQuery(false, this.entityClass, jpql, params);
		return query.getResultList();
	}
	
	/**
	 * 查询实体分页。
	 * @author Chrise 2018年11月16日
	 * @param cjpql 用于统计总记录数的JPQL语句。
	 * @param qjpql 用于读取数据的JPQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 实体分页对象。
	 */
	protected ResultPage<T> queryEntityPage(String cjpql, String qjpql, int pageIndex, int pageSize, Object... params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;
		
		TypedQuery<Long> cquery = this.createQuery(false, Long.class, cjpql, params);
		TypedQuery<T> qquery = this.createQuery(false, this.entityClass, qjpql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);
		
		return new ResultPage<>(qquery.getResultList(),pageIndex, pageSize, cquery.getSingleResult());
	}
	
	/**
	 * 查询实体分页。
	 * @author Chrise 2018年11月16日
	 * @param cjpql 用于统计总记录数的JPQL语句。
	 * @param qjpql 用于读取数据的JPQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 实体分页对象。
	 */
	protected ResultPage<T> queryEntityPage(String cjpql, String qjpql, int pageIndex, int pageSize, Map<String, Object> params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;
		
		TypedQuery<Long> cquery = this.createQuery(false, Long.class, cjpql, params);
		TypedQuery<T> qquery = this.createQuery(false, this.entityClass, qjpql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		return new ResultPage<>(qquery.getResultList(),pageIndex, pageSize, cquery.getSingleResult());
	}
	
	/**
	 * 查询布尔。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 布尔对象。
	 */
	protected Boolean queryBoolean(String jpql, Object... params) {
		TypedQuery<Boolean> query = this.createQuery(false, Boolean.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询布尔。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 布尔对象。
	 */
	protected Boolean queryBoolean(String jpql, Map<String, Object> params) {
		TypedQuery<Boolean> query = this.createQuery(false, Boolean.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询整型。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 整型对象。
	 */
	protected Integer queryInteger(String jpql, Object... params) {
		TypedQuery<Integer> query = this.createQuery(false, Integer.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询整型。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 整型对象。
	 */
	protected Integer queryInteger(String jpql, Map<String, Object> params) {
		TypedQuery<Integer> query = this.createQuery(false, Integer.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询长整型。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 长整型对象。
	 */
	protected Long queryLong(String jpql, Object... params) {
		TypedQuery<Long> query = this.createQuery(false, Long.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询长整型。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 长整型对象。
	 */
	protected Long queryLong(String jpql, Map<String, Object> params) {
		TypedQuery<Long> query = this.createQuery(false, Long.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询浮点型。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 浮点型对象。
	 */
	protected Double queryDouble(String jpql, Object... params) {
		TypedQuery<Double> query = this.createQuery(false, Double.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询浮点型。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 浮点型对象。
	 */
	protected Double queryDouble(String jpql, Map<String, Object> params) {
		TypedQuery<Double> query = this.createQuery(false, Double.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询日期。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 日期对象。
	 */
	protected Date queryDate(String jpql, Object... params) {
		TypedQuery<Date> query = this.createQuery(false, Date.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询日期。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 日期对象。
	 */
	protected Date queryDate(String jpql, Map<String, Object> params) {
		TypedQuery<Date> query = this.createQuery(false, Date.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询字符串。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 字符串对象。
	 */
	protected String queryString(String jpql, Object... params) {
		TypedQuery<String> query = this.createQuery(false, String.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询字符串。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 字符串对象。
	 */
	protected String queryString(String jpql, Map<String, Object> params) {
		TypedQuery<String> query = this.createQuery(false, String.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询字符串集合。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 字符串集合对象。
	 */
	protected List<String> queryStringList(String jpql, Object... params) {
		TypedQuery<String> query = this.createQuery(false, String.class, jpql, params);
		return query.getResultList();
	}
	
	/**
	 * 查询字符串集合。
	 * @author Chrise 2018年11月16日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 字符串集合对象。
	 */
	protected List<String> queryStringList(String jpql, Map<String, Object> params) {
		TypedQuery<String> query = this.createQuery(false, String.class, jpql, params);
		return query.getResultList();
	}
	
	/**
	 * 查询MAP（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月19日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return MAP对象。
	 */
	protected Map<String, Object> queryMap(String jpql, Object... params) {
		TypedQuery<Map> query = this.createQuery(true, Map.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询MAP（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月19日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return MAP对象。
	 */
	protected Map<String, Object> queryMap(String jpql, Map<String, Object> params) {
		TypedQuery<Map> query = this.createQuery(true, Map.class, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	/**
	 * 查询MAP集合（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月19日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return MAP集合对象。
	 */
	protected List<Map<String, Object>> queryMapList(String jpql, Object... params) {
		Query query = this.createQuery(true, Map.class, jpql, params);
		return query.getResultList();
	}
	
	/**
	 * 查询MAP集合（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月19日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return MAP集合对象。
	 */
	protected List<Map<String, Object>> queryMapList(String jpql, Map<String, Object> params) {
		Query query = this.createQuery(true, Map.class, jpql, params);
		return query.getResultList();
	}
	
	/**
	 * 查询MAP分页（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月19日
	 * @param cjpql 用于统计总记录数的JPQL语句。
	 * @param qjpql 用于读取数据的JPQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return MAP分页对象。
	 */
	protected ResultPage<Map<String, Object>> queryMapPage(String cjpql, String qjpql, int pageIndex, int pageSize, Object... params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		TypedQuery<Long> cquery = this.createQuery(false, Long.class, cjpql, params);
		Query qquery = this.createQuery(true, Map.class, qjpql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		ResultPage<Map<String, Object>> po = new ResultPage<>(qquery.getResultList(),pageIndex, pageSize, cquery.getSingleResult());
		return po;
	}

	/**
	 * 查询MAP分页（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月19日
	 * @param cjpql 用于统计总记录数的JPQL语句。
	 * @param qjpql 用于读取数据的JPQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return MAP分页对象。
	 */
	protected ResultPage<Map<String, Object>> queryMapPage(String cjpql, String qjpql, int pageIndex, int pageSize, Map<String, Object> params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		TypedQuery<Long> cquery = this.createQuery(false, Long.class, cjpql, params);
		Query qquery = this.createQuery(true, Map.class, qjpql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		ResultPage<Map<String, Object>> po = new ResultPage<>(qquery.getResultList(),pageIndex, pageSize, cquery.getSingleResult());
		return po;
	}

	/**
	 * 查询自定义封装（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月19日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 自定义封装对象。
	 */
	protected <C> C queryCustomBean(Class<C> cls, String jpql, Object... params) {
		TypedQuery<C> query = this.createQuery(true, cls, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询自定义封装（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月19日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 自定义封装对象。
	 */
	protected <C> C queryCustomBean(Class<C> cls, String jpql, Map<String, Object> params) {
		TypedQuery<C> query = this.createQuery(true, cls, jpql, params);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询自定义封装集合（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月20日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 自定义封装集合对象。
	 */
	protected <C> List<C> queryCustomBeanList(Class<C> cls, String jpql, Object... params) {
		TypedQuery<C> query = this.createQuery(true, cls, jpql, params);
		return query.getResultList();
	}

	/**
	 * 查询自定义封装集合（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月20日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 自定义封装集合对象。
	 */
	protected <C> List<C> queryCustomBeanList(Class<C> cls, String jpql, Map<String, Object> params) {
		TypedQuery<C> query = this.createQuery(true, cls, jpql, params);
		return query.getResultList();
	}

	/**
	 * 查询自定义封装分页（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月20日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param cjpql 用于统计总记录数的JPQL语句。
	 * @param qjpql 用于读取数据的JPQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 自定义封装分页对象。
	 */
	protected <C> ResultPage<C> queryCustomBeanPage(Class<C> cls, String cjpql, String qjpql, int pageIndex, int pageSize, Object... params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		TypedQuery<Long> cquery = this.createQuery(false, Long.class, cjpql, params);
		TypedQuery<C> qquery = this.createQuery(true, cls, qjpql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		ResultPage<C> po = new ResultPage<>(qquery.getResultList(),pageIndex, pageSize, cquery.getSingleResult());
		return po;
	}

	/**
	 * 查询自定义封装分页（必须使用AS为每个列指定别名）。
	 * @author Chrise 2018年11月20日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param cjpql 用于统计总记录数的JPQL语句。
	 * @param qjpql 用于读取数据的JPQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 自定义封装分页对象。
	 */
	protected <C> ResultPage<C> queryCustomBeanPage(Class<C> cls, String cjpql, String qjpql, int pageIndex, int pageSize, Map<String, Object> params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		TypedQuery<Long> cquery = this.createQuery(false, Long.class, cjpql, params);
		TypedQuery<C> qquery = this.createQuery(true, cls, qjpql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		ResultPage<C> po = new ResultPage<>(qquery.getResultList(),pageIndex, pageSize, cquery.getSingleResult());
		return po;
	}

	/**
	 * 查询实体。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 实体对象。
	 */
	protected T queryEntityBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(false, this.entityClass, sql, params);
		try {
			return (T)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询实体。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 实体对象。
	 */
	protected T queryEntityBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(false, this.entityClass, sql, params);
		try {
			return (T)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询实体集合。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 实体集合对象。
	 */
	protected List<T> queryEntityListBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(false, this.entityClass, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询实体集合。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 实体集合对象。
	 */
	protected List<T> queryEntityListBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(false, this.entityClass, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询实体分页。
	 * @author Chrise 2018年11月16日
	 * @param csql 用于统计总记录数的SQL语句。
	 * @param qsql 用于读取数据的SQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 实体分页对象。
	 */
	protected ResultPage<T> queryEntityPageBySql(String csql, String qsql, int pageIndex, int pageSize, Object... params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		Query cquery = this.createSQLQuery(true, null, csql, params);
		Query qquery = this.createSQLQuery(false, this.entityClass, qsql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		ResultPage<T> po = new ResultPage(qquery.getResultList(),pageIndex, pageSize, Long.parseLong(cquery.getSingleResult().toString()));
		return po;
	}

	/**
	 * 查询实体分页。
	 * @author Chrise 2018年11月16日
	 * @param csql 用于统计总记录数的SQL语句。
	 * @param qsql 用于读取数据的SQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 实体分页对象。
	 */
	protected ResultPage<T> queryEntityPageBySql(String csql, String qsql, int pageIndex, int pageSize, Map<String, Object> params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		Query cquery = this.createSQLQuery(true, null, csql, params);
		Query qquery = this.createSQLQuery(false, this.entityClass, qsql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		ResultPage<T> po = new ResultPage(qquery.getResultList(),pageIndex, pageSize, Long.parseLong(cquery.getSingleResult().toString()));
		return po;
	}

	/**
	 * 查询布尔。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 布尔对象。
	 */
	protected Boolean queryBooleanBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Boolean)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询布尔。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 布尔对象。
	 */
	protected Boolean queryBooleanBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Boolean)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询整型。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 整型对象。
	 */
	protected Integer queryIntegerBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Integer)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询整型。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 整型对象。
	 */
	protected Integer queryIntegerBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Integer)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询长整型。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 长整型对象。
	 */
	protected Long queryLongBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Long)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询长整型。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 长整型对象。
	 */
	protected Long queryLongBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Long)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询浮点型。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 浮点型对象。
	 */
	protected Double queryDoubleBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Double)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询浮点型。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 浮点型对象。
	 */
	protected Double queryDoubleBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Double)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询日期（仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 日期对象。
	 */
	protected Date queryDateBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Date)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询日期（仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 日期对象。
	 */
	protected Date queryDateBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (Date)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询字符串。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 字符串对象。
	 */
	protected String queryStringBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (String)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询字符串。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 字符串对象。
	 */
	protected String queryStringBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		try {
			return (String)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询字符串集合。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 字符串集合对象。
	 */
	protected List<String> queryStringListBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询字符串集合。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 字符串集合对象。
	 */
	protected List<Double> queryDoubleListBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询字符串集合。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 字符串集合对象。
	 */
	protected List<String> queryStringListBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询字符串集合。
	 * @author Chrise 2018年11月16日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 字符串集合对象。
	 */
	protected List<Double> queryDoubleListBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询MAP（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月19日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return MAP对象。
	 */
	protected Map<String, Object> queryMapBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, Map.class, sql, params);
		try {
			return (Map<String, Object>)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询MAP（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月19日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return MAP对象。
	 */
	protected Map<String, Object> queryMapBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, Map.class, sql, params);
		try {
			return (Map<String, Object>)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询MAP集合（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月19日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return MAP集合对象。
	 */
	protected List<Map<String, Object>> queryMapListBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, Map.class, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询MAP集合（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月19日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return MAP集合对象。
	 */
	protected List<Map<String, Object>> queryMapListBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, Map.class, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询MAP分页（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2019年12月12日
	 * @param sql 用于读取数据的SQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 分页MAP集合对象。
	 */
	protected List<Map<String, Object>> queryMapPageBySql(String sql, int pageIndex, int pageSize, Object... params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		Query query = this.createSQLQuery(true, Map.class, sql, params);
		query.setFirstResult(pageIndex * pageSize);
		query.setMaxResults(pageSize);

		return query.getResultList();
	}

	/**
	 * 查询MAP分页（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2019年12月12日
	 * @param sql 用于读取数据的SQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 分页MAP集合对象。
	 */
	protected List<Map<String, Object>> queryMapPageBySql(String sql, int pageIndex, int pageSize, Map<String, Object> params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		Query query = this.createSQLQuery(true, Map.class, sql, params);
		query.setFirstResult(pageIndex * pageSize);
		query.setMaxResults(pageSize);

		return query.getResultList();
	}

	/**
	 * 查询MAP分页（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月19日
	 * @param csql 用于统计总记录数的SQL语句。
	 * @param qsql 用于读取数据的SQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return MAP分页对象。
	 */
	protected ResultPage<Map<String, Object>> queryMapPageBySql(String csql, String qsql, int pageIndex, int pageSize, Object... params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		Query cquery = this.createSQLQuery(true, null, csql, params);
		Query qquery = this.createSQLQuery(true, Map.class, qsql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		ResultPage<Map<String, Object>> po = new ResultPage(qquery.getResultList(),pageIndex, pageSize, Long.parseLong(cquery.getSingleResult().toString()));
		return po;
	}

	/**
	 * 查询MAP分页（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月19日
	 * @param csql 用于统计总记录数的SQL语句。
	 * @param qsql 用于读取数据的SQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return MAP分页对象。
	 */
	protected ResultPage<Map<String, Object>> queryMapPageBySql(String csql, String qsql, int pageIndex, int pageSize, Map<String, Object> params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		Query cquery = this.createSQLQuery(true, null, csql, params);
		Query qquery = this.createSQLQuery(true, Map.class, qsql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);
		
		ResultPage<Map<String, Object>> po = new ResultPage(qquery.getResultList(),pageIndex, pageSize, Long.parseLong(cquery.getSingleResult().toString()));
		return po;
	}

	/**
	 * 查询自定义封装（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月19日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 自定义封装对象。
	 */
	protected <C> C queryCustomBeanBySql(Class<C> cls, String sql, Object... params) {
		Query query = this.createSQLQuery(true, cls, sql, params);
		try {
			return (C)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询自定义封装（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月19日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 自定义封装对象。
	 */
	protected <C> C queryCustomBeanBySql(Class<C> cls, String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, cls, sql, params);
		try {
			return (C)query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	/**
	 * 查询自定义封装集合（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月20日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 自定义封装集合对象。
	 */
	protected <C> List<C> queryCustomBeanListBySql(Class<C> cls, String sql, Object... params) {
		Query query = this.createSQLQuery(true, cls, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询自定义封装集合（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月20日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 自定义封装集合对象。
	 */
	protected <C> List<C> queryCustomBeanListBySql(Class<C> cls, String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, cls, sql, params);
		return query.getResultList();
	}

	/**
	 * 查询自定义封装分页（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月20日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param csql 用于统计总记录数的SQL语句。
	 * @param qsql 用于读取数据的SQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 自定义封装分页对象。
	 */
	protected <C> ResultPage<C> queryCustomBeanPageBySql(Class<C> cls, String csql, String qsql, int pageIndex, int pageSize, Object... params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		Query cquery = this.createSQLQuery(true, null, csql, params);
		Query qquery = this.createSQLQuery(true, cls, qsql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);
		return new ResultPage(qquery.getResultList(),pageIndex, pageSize, Long.parseLong(cquery.getSingleResult().toString()));
	}

	/**
	 * 查询自定义封装分页（返回Date对象属性仅支持Datetime数据类型）。
	 * @author Chrise 2018年11月20日
	 * @param <C> 自定义封装类型。
	 * @param cls 自定义封装类型对象。
	 * @param csql 用于统计总记录数的SQL语句。
	 * @param qsql 用于读取数据的SQL语句。
	 * @param pageIndex 基于0的分页索引，小于0时将默认为{ DEF_PAGE_INDEX}。
	 * @param pageSize 分页大小，小于等于0时将默认为{ DEF_PAGE_SIZE}。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 自定义封装分页对象。
	 */
	protected <C> ResultPage<C> queryCustomBeanPageBySql(Class<C> cls, String csql, String qsql, int pageIndex, int pageSize, Map<String, Object> params) {
		if (pageIndex < 0) pageIndex = DEF_PAGE_INDEX;
		if (pageSize <= 0) pageSize = DEF_PAGE_SIZE;

		Query cquery = this.createSQLQuery(true, null, csql, params);
		Query qquery = this.createSQLQuery(true, cls, qsql, params);
		qquery.setFirstResult(pageIndex * pageSize);
		qquery.setMaxResults(pageSize);

		return new ResultPage(qquery.getResultList(),pageIndex, pageSize, Long.parseLong(cquery.getSingleResult().toString()));
	}
	
	/**
	 * 执行更新。
	 * @author Chrise 2018年11月20日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 更新操作影响的行数。
	 */
	protected int executeUpdate(String jpql, Object... params) {
		Query query = this.createQuery(true, null, jpql, params);
		return query.executeUpdate();
	}
	
	/**
	 * 执行更新。
	 * @author Chrise 2018年11月20日
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 更新操作影响的行数。
	 */
	protected int executeUpdate(String jpql, Map<String, Object> params) {
		Query query = this.createQuery(true, null, jpql, params);
		return query.executeUpdate();
	}
	
	/**
	 * 执行更新。
	 * @author Chrise 2018年11月20日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 更新操作影响的行数。
	 */
	protected int executeUpdateBySql(String sql, Object... params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		return query.executeUpdate();
	}
	
	/**
	 * 执行更新。
	 * @author Chrise 2018年11月20日
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 更新操作影响的行数。
	 */
	protected int executeUpdateBySql(String sql, Map<String, Object> params) {
		Query query = this.createSQLQuery(true, null, sql, params);
		return query.executeUpdate();
	}
	
	/**
	 * 执行存储过程。
	 * @author Chrise 2018年11月12日
	 * @param name 将要执行的存储过程名称。
	 * @param params 将要传入存储过程的参数数组，参数通过索引设置。
	 * @return 输出参数值集合，集合的KEY值为参数索引。
	 */
	protected Map<Integer, Object> executeProcedure(String name, Object... params) {
		List<Integer> indexs = new ArrayList<Integer>();
		StoredProcedureQuery query = createProcedureQuery(null, indexs, name, params);
		query.execute();
		
		Map<Integer, Object> results = null;
		if (!indexs.isEmpty()) {
			results = new HashMap<Integer, Object>();
			for (Integer key : indexs) {
				results.put(key, query.getOutputParameterValue(key));
			}
		}
		return results;
	}
	
	/**
	 * 执行存储过程。
	 * @author Chrise 2018年11月12日
	 * @param name 将要执行的存储过程名称。
	 * @param params 将要传入存储过程的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 输出参数值集合，集合的KEY值为参数名称。
	 */
	protected Map<String, Object> executeProcedure(String name, Map<String, Object> params) {
		List<String> names = new ArrayList<String>();
		StoredProcedureQuery query = createProcedureQuery(null, names, name, params);
		query.execute();
		
		Map<String, Object> results = null;
		if (!names.isEmpty()) {
			results = new HashMap<String, Object>();
			for (String key : names) {
				results.put(key, query.getOutputParameterValue(key));
			}
		}
		return results;
	}
	
	/**
	 * 创建查询。
	 * @author Chrise 2018年11月20日
	 * @param <R> 结果集类型。
	 * @param custom 是否自定义结果集类型。
	 * @param cls 结果集类型对象。
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 查询对象。
	 */
	private <R> TypedQuery<R> createQuery(boolean custom, Class<R> cls, String jpql, Object... params) {
		TypedQuery<R> query = null;
		if (custom) {
			query = (TypedQuery<R>)this.entityManager.createQuery(jpql);
			if (cls != null) {
				if (cls == Map.class) query.unwrap(QueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
				else query.unwrap(QueryImpl.class).setResultTransformer(Transformers.aliasToBean(cls));
			}
		} else query = this.entityManager.createQuery(jpql, cls);
		if (params != null && params.length > 0) {
			for (int index = 0; index < params.length; index ++) {
				Object param = params[index];
				if (param.toString() == "class java.util.Date") {
					query.setParameter(index, (Date)param, TemporalType.DATE);
				} else {
					query.setParameter(index, param);
				}
			}
		}
		return query;
	}
	
	/**
	 * 创建查询。
	 * @author Chrise 2018年11月20日
	 * @param <R> 结果集类型。
	 * @param custom 是否自定义结果集类型。
	 * @param cls 结果集类型对象。
	 * @param jpql 将要执行的JPQL语句。
	 * @param params 将要传入JPQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 查询对象。
	 */
	private <R> TypedQuery<R> createQuery(boolean custom, Class<R> cls, String jpql, Map<String, Object> params) {
		TypedQuery<R> query = null;
		if (custom) {
			query = (TypedQuery<R>)this.entityManager.createQuery(jpql);
			if (cls != null) {
				if (cls == Map.class) query.unwrap(QueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
				else query.unwrap(QueryImpl.class).setResultTransformer(Transformers.aliasToBean(cls));
			}
		} else query = this.entityManager.createQuery(jpql, cls);
		if (params != null && !params.isEmpty()) {
			Set<String> names = params.keySet();
			for (String name : names) {
				Object param = params.get(name);
				if (param.toString() == "class java.util.Date") {
					query.setParameter(name, (Date)param, TemporalType.DATE);
				} else {
					query.setParameter(name, param);
				}
			}
		}
		return query;
	}
	
	/**
	 * 创建查询。
	 * @author Chrise 2018年11月20日
	 * @param <R> 结果集类型。
	 * @param custom 是否自定义结果集类型。
	 * @param cls 结果集类型对象。
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数数组，参数通过基于0的索引设置。
	 * @return 查询对象。
	 */
	private <R> Query createSQLQuery(boolean custom, Class<R> cls, String sql, Object... params) {
		Query query = null;
		if (custom) {
			query = this.entityManager.createNativeQuery(sql);
			if (cls != null) {
				if (cls == Map.class) query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
				else query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(cls));
			}
		} else query = this.entityManager.createNativeQuery(sql, cls);
		
		if (params != null && params.length > 0) {
			for (int index = 0; index < params.length; index ++) {
				Object param = params[index];
				if (param.toString() == "class java.util.Date") {
					query.setParameter(index, (Date)param, TemporalType.DATE);
				} else {
					query.setParameter(index, param);
				}
			}
		}
		return query;
	}
	
	/**
	 * 创建查询。
	 * @author Chrise 2018年11月20日
	 * @param <R> 结果集类型。
	 * @param custom 是否自定义结果集类型。
	 * @param cls 结果集类型对象。
	 * @param sql 将要执行的SQL语句。
	 * @param params 将要传入SQL语句的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 查询对象。
	 */
	private <R> Query createSQLQuery(boolean custom, Class<R> cls, String sql, Map<String, Object> params) {
		Query query = null;
		if (custom) {
			query = this.entityManager.createNativeQuery(sql);
			if (cls != null) {
				if (cls == Map.class) query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
				else query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(cls));
			}
		} else query = this.entityManager.createNativeQuery(sql, cls);
		
		if (params != null && !params.isEmpty()) {
			Set<String> names = params.keySet();
			for (String name : names) {
				Object param = params.get(name);
				if (param.toString() == "class java.util.Date") {
					query.setParameter(name, (Date)param, TemporalType.DATE);
				} else {
					query.setParameter(name, param);
				}
			}
		}
		return query;
	}
	
	/**
	 * 创建存储过程查询。
	 * @author Chrise 2018年11月12日
	 * @param <R> 结果集类型。
	 * @param clss 结果集类型对象数组。
	 * @param indexs 输出参数索引集合。
	 * @param name 存储过程名。
	 * @param params 将要传入存储过程的参数数组，参数通过索引设置。
	 * @return 存储过程查询对象。
	 */
	private <R> StoredProcedureQuery createProcedureQuery(Class<R>[] clss, List<Integer> indexs, String name, Object... params) {
		//创建存储过程查询对象
		StoredProcedureQuery query = null;
		if (clss == null) query = this.entityManager.createStoredProcedureQuery(name);
		else query = this.entityManager.createStoredProcedureQuery(name, clss);
		
		//注册参数
		if (params != null && params.length > 0) {
			int index = 0;
			for (Object object : params) {
				switch (object.getClass().toString()) {
					case "class java.lang.Class":	//类类型-用于输出参数
						{
							switch (object.toString()) {
								case "class java.lang.Boolean":	//布尔类型
									query.registerStoredProcedureParameter(index, Boolean.class, ParameterMode.OUT);
									break;
								case "class java.lang.Integer":	//整型
									query.registerStoredProcedureParameter(index, Integer.class, ParameterMode.OUT);
									break;
								case "class java.lang.Long":	//长整型
									query.registerStoredProcedureParameter(index, Long.class, ParameterMode.OUT);
									break;
								case "class java.lang.Double":	//双精度浮点数
									query.registerStoredProcedureParameter(index, Double.class, ParameterMode.OUT);
									break;
								case "class java.lang.String":	//字符串
									query.registerStoredProcedureParameter(index, String.class, ParameterMode.OUT);
									break;
								case "class java.util.Date":	//日期
									query.registerStoredProcedureParameter(index, Date.class, ParameterMode.OUT);
									break;
								default:
									continue;					//忽略未列举的类型
							}
							
							indexs.add(index);
						}
						break;
					case "class java.lang.Boolean":	//布尔类型
						query.registerStoredProcedureParameter(index, Boolean.class, ParameterMode.IN).setParameter(index, (Boolean)object);
						break;
					case "class java.lang.Integer":	//整型
						query.registerStoredProcedureParameter(index, Integer.class, ParameterMode.IN).setParameter(index, (Integer)object);
						break;
					case "class java.lang.Long":	//长整型
						query.registerStoredProcedureParameter(index, Long.class, ParameterMode.IN).setParameter(index, (Long)object);
						break;
					case "class java.lang.Double":	//双精度浮点数
						query.registerStoredProcedureParameter(index, Double.class, ParameterMode.IN).setParameter(index, (Double)object);
						break;
					case "class java.lang.String":	//字符串
						query.registerStoredProcedureParameter(index, String.class, ParameterMode.IN).setParameter(index, (String)object);
						break;
					case "class java.util.Date":	//日期
						query.registerStoredProcedureParameter(index, Date.class, ParameterMode.IN).setParameter(index, (Date)object, TemporalType.DATE);
						break;
					default:
						continue;					//忽略未列举的类型
				}
				
				index ++;
			}
		}
		
		return query;
	}
	
	/**
	 * 创建存储过程查询。
	 * @author Chrise 2018年11月12日
	 * @param <R> 结果集类型。
	 * @param clss 结果集类型对象数组。
	 * @param names 输出参数名称集合。
	 * @param name 存储过程名。
	 * @param params 将要传入存储过程的参数集合，参数通过名称（集合的KEY值）设置。
	 * @return 存储过程查询对象。
	 */
	private <R> StoredProcedureQuery createProcedureQuery(Class<R>[] clss, List<String> names, String name, Map<String, Object> params) {
		//创建存储过程查询对象
		StoredProcedureQuery query = null;
		if (clss == null) query = this.entityManager.createStoredProcedureQuery(name);
		else query = this.entityManager.createStoredProcedureQuery(name, clss);
		
		//注册参数
		if (params != null && !params.isEmpty()) {
			Set<String> keys = params.keySet();
			for (String key : keys) {
				Object object = params.get(key);
				switch (object.getClass().toString()) {
					case "class java.lang.Class":	//类类型-用于输出参数
						{
							switch (object.toString()) {
								case "class java.lang.Boolean":	//布尔类型
									query.registerStoredProcedureParameter(key, Boolean.class, ParameterMode.OUT);
									break;
								case "class java.lang.Integer":	//整型
									query.registerStoredProcedureParameter(key, Integer.class, ParameterMode.OUT);
									break;
								case "class java.lang.Long":	//长整型
									query.registerStoredProcedureParameter(key, Long.class, ParameterMode.OUT);
									break;
								case "class java.lang.Double":	//双精度浮点数
									query.registerStoredProcedureParameter(key, Double.class, ParameterMode.OUT);
									break;
								case "class java.lang.String":	//字符串
									query.registerStoredProcedureParameter(key, String.class, ParameterMode.OUT);
									break;
								case "class java.util.Date":	//日期
									query.registerStoredProcedureParameter(key, Date.class, ParameterMode.OUT);
									break;
								default:
									continue;					//忽略未列举的类型
							}
							
							names.add(key);
						}
						break;
					case "class java.lang.Boolean":	//布尔类型
						query.registerStoredProcedureParameter(key, Boolean.class, ParameterMode.IN).setParameter(key, (Boolean)object);
						break;
					case "class java.lang.Integer":	//整型
						query.registerStoredProcedureParameter(key, Integer.class, ParameterMode.IN).setParameter(key, (Integer)object);
						break;
					case "class java.lang.Long":	//长整型
						query.registerStoredProcedureParameter(key, Long.class, ParameterMode.IN).setParameter(key, (Long)object);
						break;
					case "class java.lang.Double":	//双精度浮点数
						query.registerStoredProcedureParameter(key, Double.class, ParameterMode.IN).setParameter(key, (Double)object);
						break;
					case "class java.lang.String":	//字符串
						query.registerStoredProcedureParameter(key, String.class, ParameterMode.IN).setParameter(key, (String)object);
						break;
					case "class java.util.Date":	//日期
						query.registerStoredProcedureParameter(key, Date.class, ParameterMode.IN).setParameter(key, (Date)object, TemporalType.DATE);
						break;
					default:
						continue;					//忽略未列举的类型
				}
			}
		}
		
		return query;
	}
}
